#include "map.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#define IN_BOUNDS(_X, _Y, _MAX_X, _MAX_Y) ((_X >= 0 && _X < _MAX_X) && (_Y >= 0 && _Y < _MAX_Y))

//===============================================================
// Initializes a new map of the specified size.
//===============================================================
CMap::CMap(int nWidth, int nHeight, int nLayers)
: m_nWidth(nWidth), m_nHeight(nHeight), m_nLayers(nLayers), m_pTiles(NULL), m_nVersion(1),	m_pTileInfo(NULL)
{
	m_nStride = m_nWidth * m_nHeight; /* Size of a single layer in bytes. */
	m_nSize   = m_nStride * m_nLayers;

	m_pTiles = (TILE *)malloc(sizeof(TILE) * m_nSize);
	memset(m_pTiles, 0, sizeof(TILE) * m_nSize);
}

//===============================================================
// Loads a map from the specified file.
//===============================================================
CMap::CMap(const char *szFileName)
: m_nWidth(0), m_nHeight(0), m_nLayers(0), m_pTiles(NULL), m_nVersion(1), m_pTileInfo(NULL)
{
	FILE *f = fopen(szFileName, "rb");
	if (!f)
		return;

	char sig[4] = "\0\0\0";
	fread(sig, 3, 1, f);
	if (strcmp(sig, "PKL") == 0) // Pokemon Level
	{
		fread(&m_nVersion, 1, 1, f);
		if (m_nVersion == 1)  // V1
		{
			fread(&m_nWidth, 4, 1, f);
			fread(&m_nHeight, 4, 1, f);
			fread(&m_nLayers, 4, 1, f);

			m_nStride = m_nWidth * m_nHeight;
			m_nSize = m_nStride * m_nLayers;
			m_pTiles = (TILE *)malloc(m_nSize * sizeof(TILE));
			fread(m_pTiles, m_nSize * sizeof(TILE), 1, f);
		}
	}
	fclose(f);
}

//===============================================================
// Releases the data allocated by the map.
//===============================================================
CMap::~CMap()
{
	if (m_pTiles)
		free(m_pTiles);

	if (m_pTileInfo)
		free(m_pTileInfo);
}

//===============================================================
// Gets the tile at the specified position.
//===============================================================
TILE *CMap::At(int nX, int nY, int nZ)
{
	if ((nX < 0 || nX >= m_nWidth) || (nY < 0 || nY >= m_nHeight) || (nZ < 0 || nZ >= m_nLayers))
		return NULL;

	return &m_pTiles[(nZ * m_nStride) + (nY * m_nWidth) + nX];
}

//===============================================================
// Loads a tileset definition from the specified file and applies it to the map.
//===============================================================
void CMap::LoadTileDefinition(const char *szFileName)
{
	FILE *f = fopen(szFileName, "rb");
	if (!f)
		return;

	int nWidth, nHeight;

	char sig[4] = "\0\0\0";
	fread(sig, 3, 1, f);
	if (strcmp(sig, "PTD") == 0) // Pokemon Tileset Definition
	{
		fread(&m_nVersion, 1, 1, f);
		if (m_nVersion == 1) // V1
		{
			fread(&nWidth, 4, 1, f);
			fread(&nHeight, 4, 1, f);

			int nSize = sizeof(TILEINFO) * (nWidth * nHeight);
			TILEINFO *pTileData = (TILEINFO *)malloc(nSize);
			if (pTileData)
			{
				fread(pTileData, nSize, 1, f);
				nSize = sizeof(TILEINFO) * (m_nWidth * m_nHeight);

				if (m_pTileInfo)
					free(m_pTileInfo);

				m_pTileInfo = (TILEINFO *)malloc(nSize);
				memset(m_pTileInfo, 0, nSize);

				/* Generate the tile info for the current map. */
				for (int z = 0; z < m_nLayers; z++) 
				{
					for (int y = 0; y < m_nHeight; y++)
					{
						for (int x = 0; x < m_nWidth; x++)
						{
							TILE *t = At(x, y, z);
							if (!IN_BOUNDS(t->x, t->y, nWidth, nHeight))
								continue;

							m_pTileInfo[(y * m_nWidth) + x].flags |= pTileData[(t->y * nWidth) + t->x].flags;
						}
					}
				}
				free(pTileData);
			}
		}
	}
	fclose(f);
}

//===============================================================
// Gets the details of the tile at the specified location.
//===============================================================
TILEINFO *CMap::GetTileInfo(int nX, int nY)
{
	if ((nX < 0 || nX >= m_nWidth) || (nY < 0 || nY >= m_nHeight))
		return NULL;

	return &m_pTileInfo[(nY * m_nWidth) + nX];
}
